import * as THREE from "three";
import { IBvhNode } from "./IBvhNode";

/**
 * BVH类
 *
 * 朴素的BVH，空间划分方法是二分最长边
 */
export class BvhNode implements IBvhNode {
    lChild: BvhNode | undefined;
    rChild: BvhNode | undefined;
    boundingBox!: THREE.Box3;
    objects!: THREE.Mesh[];
    private leafNodeChildrenNumberLimit = 4;
    /**
     * 构建BVH树
     *
     * 划分方法：最长维度一分为二
     * @param objs
     * @returns
     */
    buildTree(objs: THREE.Mesh[]): BvhNode {
        // const building = new BVHBuildNode();
        this.objects = objs;
        this.computeBoundingBox();
        if (this.objects.length <= this.leafNodeChildrenNumberLimit) {
            1;
        } else {
            // 获取boundingbox最长维度
            const longestAxis = this.getLongestAxis();
            // objects元素按照维度排序
            this.objects.sort((a, b) => {
                return a.position.getComponent(longestAxis) - b.position.getComponent(longestAxis);
            });
            // objects元素按照排序分成LObjects和RObjects
            const halfLen = this.objects.length >> 1;
            // 递归构建左孩子objects[LObjects]
            this.lChild = new BvhNode().buildTree(this.objects.slice(0, halfLen));
            // 递归构建右孩子objects[RObjects]
            this.rChild = new BvhNode().buildTree(this.objects.slice(halfLen));
        }
        return this;
    }
    getIntersection(raycaster: THREE.Raycaster): THREE.Intersection[] {
        const ans: THREE.Intersection[] = [];
        this.computeBoundingBox();
        if (!raycaster.intersectObject(new THREE.Box3Helper(this.boundingBox))) {
            return ans;
        }
        if (!this.lChild && !this.rChild) {
            //ray和mesh的碰撞
            const res = raycaster.intersectObjects(this.objects);
            ans.push(...res);
        } else {
            ans.push(...this.lChild!.getIntersection(raycaster), ...this.rChild!.getIntersection(raycaster));
        }
        return ans;
    }
    computeBoundingBox() {
        if (this.boundingBox) {
            return;
        }
        this.objects[0].geometry.computeBoundingBox();
        this.boundingBox = new THREE.Box3(
            this.objects[0].position.clone().add(this.objects[0].geometry.boundingBox!.min),
            this.objects[0].position.clone().add(this.objects[0].geometry.boundingBox!.max)
        );
        if (this.objects.length > 1) {
            for (const b of this.objects) {
                b.geometry.computeBoundingBox();
                const bb = b.geometry.boundingBox!;
                this.boundingBox.union(new THREE.Box3(b.position.clone().add(bb.min), b.position.clone().add(bb.max)));
            }
        }
    }
    /**
     * 获取最长边
     * 0,1,2 = x,y,z
     */
    private getLongestAxis(): 0 | 1 | 2 {
        if (!this.boundingBox) {
            this.computeBoundingBox();
        }
        let longestAxis: 0 | 1 | 2 = 0;
        let longest = 0;
        const size: THREE.Vector3 = new THREE.Vector3();
        this.boundingBox.getSize(size);
        if (size.x > size.y) {
            longest = size.x;
            longestAxis = 0;
        } else {
            longest = size.y;
            longestAxis = 1;
        }
        if (size.z > longest) {
            longest = size.z;
            longestAxis = 2;
        }
        return longestAxis;
    }
}
